using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics;
using UnitLibrary.Managers;

#region Signature
/*
 * This file was written by Daniel Selnick
 * email: d.selnick@gmail.com
 * It is under the following license
 *  http://www.opensource.org/licenses/ms-pl.html
 */
#endregion

namespace UnitLibrary.Utility
{
    public enum GameState
    {
        PlayerDied,
        PlayerWonByPoints,
        TimeExpired,
        PointCaptured
    }
    /// <summary>
    /// Utility class that contains static function for access anywhere
    /// </summary>
    public static class Util
    {
        //private static BoundingSphere tempSphere1 =
        //    new BoundingSphere();
        //private static BoundingSphere tempSphere2 =
        //    new BoundingSphere();
        //private static BoundingBox tempBox1 =
        //    new BoundingBox();
        //private static BoundingBox tempBox2 =
        //    new BoundingBox();

        //private static RPool<GameUnit<Enemy, PhyObj>>.Node _enemyNode1;
        //private static RPool<GameUnit<Enemy, PhyObj>>.Node _enemyNode2;

        //private static RPool<GameUnit<Bullet, PhyObj>>.Node _bulletNode1;
        //private static RPool<GameUnit<Bullet, PhyObj>>.Node _bulletNode2;

        //private static RPool<GameUnit<Tower, PhyObj>>.Node _towerNode1;
        //private static RPool<GameUnit<Tower, PhyObj>>.Node _towerNode2;

        //private static GameUnit<Player, PhyObj> _player;

        //private static float _dmg1;
        //private static float _dmg2;

        //private static ContainerTypeID _containerTypeID1;
        //private static ContainerTypeID _containerTypeID2;

        //private static float _health1;
        //private static float _health2;       

        public static Random random = new Random();


        /// <summary>
        /// Generic function to add to everyobject in the whole game
        /// </summary>
        /// <param name="geom1">FirstGeom</param>
        /// <param name="geom2">SecondGeom</param>
        /// <param name="contactList"></param>
        /// <returns></returns>
        //public static bool GenericOnCollision(Geom geom1, Geom geom2, ContactList contactList)
        //{
        //    //figure out which type of object geom1 is and assign a value to it
        //    switch (geom1.ContainerTID)
        //    {
        //        case ContainerTypeID.BulletPool:
        //            _bulletNode1 = BulletManager.BulletPool.Get(geom1.NodeID);
        //            _containerTypeID1 = ContainerTypeID.BulletPool;
        //            break;
        //        case ContainerTypeID.EnemyPool:
        //            _enemyNode1 = MainGameScreen.enemyManager.EnemyPool.Get(geom1.NodeID);
        //            _containerTypeID1 = ContainerTypeID.EnemyPool;
        //            break;
        //        case ContainerTypeID.TowerPool:
        //            _towerNode1 = TowerManager.TowerPool.Get(geom1.NodeID);
        //            _containerTypeID1 = ContainerTypeID.TowerPool;
        //            break;
        //        case ContainerTypeID.PlayerObject:
        //            _player = PlayerManager.player;
        //            _containerTypeID1 = ContainerTypeID.PlayerObject;
        //            break;
        //        case ContainerTypeID.Border:
        //            _containerTypeID1 = ContainerTypeID.Border;
        //            break;
        //        default:
        //            throw new Exception("Geom1 not represented");
        //            break;
        //    }
        //    //figure out what geom2 is and assign a value to it
        //    switch (geom2.ContainerTID)
        //    {
        //        case ContainerTypeID.BulletPool:
        //            _bulletNode2 = BulletManager.BulletPool.Get(geom2.NodeID);
        //            _containerTypeID2 = ContainerTypeID.BulletPool;
        //            break;
        //        case ContainerTypeID.EnemyPool:
        //            _enemyNode2 = EnemyManager.EnemyPool.Get(geom2.NodeID);
        //            _containerTypeID2 = ContainerTypeID.EnemyPool;
        //            break;
        //        case ContainerTypeID.TowerPool:
        //            _towerNode2 = TowerManager.TowerPool.Get(geom2.NodeID);
        //            _containerTypeID2 = ContainerTypeID.TowerPool;
        //            break;
        //        case ContainerTypeID.PlayerObject:
        //            _player = PlayerManager.player;
        //            _containerTypeID2 = ContainerTypeID.PlayerObject;
        //            break;
        //        case ContainerTypeID.Border:
        //            _containerTypeID2 = ContainerTypeID.Border;
        //            break;
        //        default:
        //            throw new Exception("Geom2 not represented");
        //            break;
        //    }

        //    //Switch through the first type
        //    switch (_containerTypeID1)
        //    {
        //        case ContainerTypeID.BulletPool:
        //            //make sure bullet isn't null
        //            if (_bulletNode1 == null)
        //                throw new Exception("Bullet is null");
        //            //go through different combonations
        //            switch (_containerTypeID2)
        //            {
        //                case ContainerTypeID.BulletPool:
        //                    //Bullets don't intersect as of yet
        //                    return false;

        //                case ContainerTypeID.EnemyPool:
        //                    ApplyDamage(_bulletNode1, _enemyNode2);
        //                    GameScreens.MainGameScreen.DisposeBullet(_bulletNode1.NodeIndex);
        //                    return true;

        //                case ContainerTypeID.PlayerObject:
        //                    //Bullets don't intersect with player
        //                    return false;

        //                case ContainerTypeID.TowerPool:
        //                    //Bullets don't intersect with towers
        //                    //todo: possible gameplay feature
        //                    return false;

        //                case ContainerTypeID.Border:
        //                    return true;

        //            }
        //            return true;

        //        case ContainerTypeID.EnemyPool:
        //            //using the first enemy node
        //            if (_enemyNode1 == null)
        //                throw new Exception("Enemy can't be null");
        //            switch (_containerTypeID2)
        //            {
        //                //using the second of all these
        //                case ContainerTypeID.BulletPool:
        //                    //apply damage from bullet to enemy
        //                    //todo: maybe cause of double damage, investigate
        //                    ApplyDamage(_bulletNode2, _enemyNode1);
        //                    GameScreens.MainGameScreen.DisposeBullet(_bulletNode2.NodeIndex);
        //                    return true;

        //                case ContainerTypeID.EnemyPool:
        //                    //enemies don't collide
        //                    return false;

        //                case ContainerTypeID.PlayerObject:
        //                    //Enemy attacked player, deal damage
        //                    ApplyDamage(_enemyNode1, _player);
        //                    return true;

        //                case ContainerTypeID.TowerPool:
        //                    TowerManager.FireOnCollision(_towerNode2, _enemyNode1);
        //                    //make sure its not the sensor
        //                    if (!geom2.IsSensor)
        //                        //Have enemy deal damage to tower
        //                        ApplyDamage(_enemyNode1, _towerNode2);
        //                    return true;

        //                case ContainerTypeID.Border:
        //                    return true;

        //            }
        //            return true;

        //        case ContainerTypeID.PlayerObject:
        //            switch (_containerTypeID2)
        //            {
        //                case ContainerTypeID.BulletPool:
        //                    return false;

        //                case ContainerTypeID.EnemyPool:
        //                    //player ran into enemy
        //                    ApplyDamage(_enemyNode2, _player);
        //                    return true;

        //                case ContainerTypeID.PlayerObject:
        //                    return false;

        //                case ContainerTypeID.TowerPool:
        //                    return false;

        //                case ContainerTypeID.Border:
        //                    return true;

        //            }
        //            return true;

        //        case ContainerTypeID.TowerPool:
        //            switch (_containerTypeID2)
        //            {
        //                case ContainerTypeID.BulletPool:
        //                    return false;

        //                case ContainerTypeID.EnemyPool:
        //                    TowerManager.FireOnCollision(_towerNode1, _enemyNode2);
        //                    //Make sure it isn't the tower sensor
        //                    if (!geom1.IsSensor)
        //                        ApplyDamage(_enemyNode2, _towerNode1);

        //                    return true;
        //                case ContainerTypeID.PlayerObject:
        //                    return false;

        //                case ContainerTypeID.TowerPool:
        //                    return false;

        //                case ContainerTypeID.Border:
        //                    return false;

        //            }
        //            return true;

        //        case ContainerTypeID.Border:
        //            switch (_containerTypeID2)
        //            {
        //                case ContainerTypeID.BulletPool:
        //                    GameScreens.MainGameScreen.DisposeBullet(_bulletNode2.NodeIndex);
        //                    return true;

        //                case ContainerTypeID.EnemyPool:
        //                    return true;

        //                case ContainerTypeID.PlayerObject:
        //                    return true;

        //                case ContainerTypeID.TowerPool:
        //                    return false;

        //                case ContainerTypeID.Border:
        //                    return false;

        //            }
        //            return true;
        //    }
        //    return true;
        //}

        ///// <summary>
        ///// Apply damage from enemy to tower
        ///// </summary>
        ///// <param name="_enemyNode1"></param>
        ///// <param name="_towerNode2"></param>
        //private static void ApplyDamage(RPool<GameUnit<Enemy, PhyObj>>.Node enemyNode, RPool<GameUnit<Tower, PhyObj>>.Node towerNode)
        //{
        //    towerNode.Item.UType.HealthCurrent -= enemyNode.Item.UType.AttackDamageCurrent;
        //    if (towerNode.Item.UType.HealthCurrent < towerNode.Item.UType.HealthMin)
        //        GameScreens.MainGameScreen.DisposeTower(towerNode.NodeIndex);
        //}

        ///// <summary>
        ///// Apply damage from enemy to player
        ///// </summary>
        ///// <param name="_enemyNode1"></param>
        ///// <param name="_player"></param>
        //private static void ApplyDamage(RPool<GameUnit<Enemy, PhyObj>>.Node enemyNode, GameUnit<Player, PhyObj> player)
        //{
        //    player.UType.HealthCurrent -= enemyNode.Item.UType.AttackDamageCurrent;
        //}

        ///// <summary>
        ///// //todo: possibly implement
        ///// Apply damage from enemy to bullet
        ///// </summary>
        ///// <param name="_enemyNode1"></param>
        ///// <param name="_bulletNode2"></param>
        //private static void ApplyDamage(RPool<GameUnit<Enemy, PhyObj>>.Node enemyNode, RPool<GameUnit<Bullet, PhyObj>>.Node bulletNode)
        //{
            
        //}

        ///// <summary>
        ///// Apply damage from bullet to enemy
        ///// </summary>
        ///// <param name="bulletNode1">bullet dealing damage</param>
        ///// <param name="enemyNode1">enemy receiving damage</param>
        //private static void ApplyDamage(RPool<GameUnit<Bullet, PhyObj>>.Node bulletNode, RPool<GameUnit<Enemy, PhyObj>>.Node enemyNode)
        //{
        //    //Apply damage
        //    enemyNode.Item.UType.HealthCurrent -= bulletNode.Item.UType.AttackDamageCurrent;
        //    //remove if it should be dead
        //    if (enemyNode.Item.UType.HealthCurrent < enemyNode.Item.UType.HealthMin)
        //        GameScreens.MainGameScreen.DisposeEnemy(enemyNode.NodeIndex);
        //}

        
        /// <summary>
        /// 
        /// </summary>
        public enum Direction
        {
            Up = 0,
            Down,
            Left,
            Right,
            UpLeft,
            UpRight,
            DownLeft,
            DownRight
        }

        /// <summary>
        /// The different types of objects which can collide with one another
        /// </summary>
       
        /// <summary>
        /// 
        /// </summary>
        /// <param name="launchPos"></param>
        /// <param name="attackLocation"></param>
        /// <returns></returns>
        public static float calculateDirection(Vector2 launchPos, Vector2 attackLocation)
        {
            float XDistance = launchPos.X - attackLocation.X;
            float YDistance = launchPos.Y - attackLocation.Y;
            float attackDirection = (float)Math.Atan2(YDistance, XDistance);
            return attackDirection;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Position"></param>
        /// <param name="moveSpeed"></param>
        /// <param name="moveDirection"></param>
        /// <returns></returns>
        public static Vector2 calculateNewPosition(Vector2 Position, float moveSpeed, float moveDirection)
        {
            //Move square towards mouse by closing the gap 3 pixels per update
            Position.X -= (float)(moveSpeed * Math.Cos(moveDirection));
            Position.Y -= (float)(moveSpeed * Math.Sin(moveDirection));
            return Position;
        }

        //todo: fix up the SetDirection functions to be used in game
        /*
        /// <summary>
        /// A function to set the different directions for a number of bullets
        /// </summary>
        /// <param name="attackLocation">The location for the attack to land at</param>
        /// <param name="attackDirection">The angle of the location to land at</param>
        /// <param name="numShots">the number of shots</param>
        /// <param name="dirDif">the angle in between each bullet</param>
        /// 
        public static LinkedList<float> SetDirection(float attackDirection, Bullet bullet)
        {
            int numShots = bullet.NumBullets;
            float dirDif = bullet.fdirectionDiff;
            //todo let it change just the current shots
            float dirtemp = dirDif;
            //used for making the bullets to the right of the straight line
            bool Right = true;
            //used to test if there is an even number of bullets
            bool isEven = true;
            //used to test if its the first bullet being initialized
            bool first = true;
            //used for determining the number of bullets on each side of the shot
            int numRight = numShots / 2;
            //count the number of bullets
            int counter = 0;
            //determine if its odd
            if ((numShots % 2) > 0)
                isEven = false;

            foreach (Bullet TB in _tempShotList)
            {
                _tempB.Rotation = TB.Rotation;
                //if its an even number of bullets
                if (isEven)
                {
                    //if we are creating them to the right
                    if (Right)
                    {
                        //if its the first bullet
                        //we need to do it half the normal angle
                        if (first)
                        {
                            dirtemp = dirDif / 2f;
                            _tempB.Rotation = attackDirection + dirtemp;
                            dirtemp += dirDif;
                            counter++;
                            first = false;
                        }
                        //otherwise just keep adding after the first
                        else
                        {
                            _tempB.Rotation = attackDirection + dirtemp;
                            dirtemp += dirDif;
                            counter++;
                        }
                        //when the bullets to the right are made
                        //switch to making the bullets to the left
                        if (counter == numRight)
                        {
                            Right = false;
                            first = true;
                            dirtemp = dirDif;
                        }
                    }
                    //if its to the left
                    else
                    {
                        //if its the first bullet to the left
                        //do it half the normal
                        if (first)
                        {
                            dirtemp = dirDif / 2f;
                            _tempB.Rotation = attackDirection + dirtemp;
                            dirtemp -= dirDif;
                            first = false;
                        }
                        //otherwise do as normal
                        else
                        {
                            _tempB.Rotation = attackDirection + dirtemp;
                            dirtemp -= dirDif;
                        }
                    }
                }
                //if its an odd number of bullets

                else
                {
                    //if its the first one, we
                    //will have it go straight down the middle
                    if (first)
                    {
                        _tempB.Rotation = attackDirection;
                    }
                    if (Right)
                    {

                        _tempB.Rotation = attackDirection + dirtemp;
                        dirtemp += dirDif;
                        counter++;

                        if (counter == numRight)
                        {
                            Right = false;
                            dirtemp = dirDif;
                        }
                    }
                    //making them to the left now
                    else
                    {
                        _tempB.Rotation = attackDirection + dirtemp;
                        dirtemp -= dirDif;
                    }
                }
                TB.Rotation = _tempB.Rotation;
            }
        }

        private static LinkedList<Vector2> SetDirection(Vector2 attackDirection, Bullet bullet)
        {
            _dirDiff.X = (float)Math.Cos(bullet.ddirectionDiff);
            _dirDiff.Y = (float)Math.Sin(bullet.ddirectionDiff);
            int numShots = bullet.NumBullets;
            //todo let it change just the current shots
            _dirTemp = _dirDiff;
            //used for making the bullets to the right of the straight line
            bool Right = true;
            //used to test if there is an even number of bullets
            bool isEven = true;
            //used to test if its the first bullet being initialized
            bool first = true;
            //used for determining the number of bullets on each side of the shot
            int numRight = numShots / 2;
            //count the number of bullets
            int counter = 0;
            //determine if its odd
            if ((numShots % 2) > 0)
                isEven = false;

            foreach (Bullet TB in _tempShotList)
            {
                _tempB.AttackDirection = TB.AttackDirection;
                //if its an even number of bullets
                if (isEven)
                {
                    //if we are creating them to the right
                    if (Right)
                    {
                        //if its the first bullet
                        //we need to do it half the normal angle
                        if (first)
                        {
                            _dirTemp = _dirDiff / 2f;
                            _tempB.AttackDirection = attackDirection + _dirTemp;
                            _dirTemp += _dirDiff;
                            counter++;
                            first = false;
                        }
                        //otherwise just keep adding after the first
                        else
                        {
                            _tempB.AttackDirection = attackDirection + _dirTemp;
                            _dirTemp += _dirDiff;
                            counter++;
                        }
                        //when the bullets to the right are made
                        //switch to making the bullets to the left
                        if (counter == numRight)
                        {
                            Right = false;
                            first = true;
                            _dirTemp = _dirDiff;
                        }
                    }
                    //if its to the left
                    else
                    {
                        //if its the first bullet to the left
                        //do it half the normal
                        if (first)
                        {
                            _dirTemp = _dirDiff / 2f;
                            _tempB.AttackDirection = attackDirection + _dirTemp;
                            _dirTemp -= _dirDiff;
                            first = false;
                        }
                        //otherwise do as normal
                        else
                        {
                            _tempB.AttackDirection = attackDirection + _dirTemp;
                            _dirTemp -= _dirDiff;
                        }
                    }
                }
                //if its an odd number of bullets

                else
                {
                    //if its the first one, we
                    //will have it go straight down the middle
                    if (first)
                    {
                        _tempB.AttackDirection = attackDirection;
                    }
                    if (Right)
                    {

                        _tempB.AttackDirection = attackDirection + _dirTemp;
                        _dirTemp += _dirDiff;
                        counter++;

                        if (counter == numRight)
                        {
                            Right = false;
                            _dirTemp = _dirDiff;
                        }
                    }
                    //making them to the left now
                    else
                    {
                        _tempB.AttackDirection = attackDirection + _dirTemp;
                        _dirTemp -= _dirDiff;
                    }
                }
                TB.AttackDirection = _tempB.AttackDirection;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="center1"></param>
        /// <param name="furthestPoint1"></param>
        /// <param name="BoundingType1"></param>
        /// <param name="center2"></param>
        /// <param name="furthestPoint2"></param>
        /// <param name="BoundingType2"></param>
        /// <returns></returns>
        public static bool CollideTwo(Vector3 center1, Vector3 furthestPoint1, string BoundingType1, Vector3 center2, Vector3 furthestPoint2, string BoundingType2)
        {
            tempBox1.Min = center1;
            tempBox1.Max = furthestPoint1;
            tempBox2.Min = center2;
            tempBox2.Max = furthestPoint2;

            tempSphere1.Center = center1;
            tempSphere1.Radius = Vector3.Distance(center1, furthestPoint1);
            tempSphere2.Center = center2;
            tempSphere2.Radius = Vector3.Distance(center2, furthestPoint2);
            //todo: change from string comparison to boolean comparison!
                if (BoundingType1 == "boundingsphere")
                {
                    if (BoundingType2 == "boundingsphere")
                    {
                        return (tempSphere1.Intersects(tempSphere2)); 
                    }

                    else if (BoundingType2 == "boundingbox")
                    {
                        return (tempSphere1.Intersects(tempBox2));
                    }
                    else
                    {
                        return false;
                    }
                }

                else if (BoundingType1 == "boundingbox")
                {
                    if (BoundingType2 == "boundingsphere")
                    {
                        return (tempBox1.Intersects(tempSphere2));
                    }
                    else if (BoundingType2 == "boundingbox")
                    {
                        return (tempBox1.Intersects(tempBox2));                            
                    }
                    else
                    {
                        return false;
                    }
                }
                else
                {
                    return false;
                }
        }

        //todo: implement this
        //todo: put all the data containers into the game screen
        /// <summary>
        /// Public function that will listen to all collision events
        /// and follow through with applying damage, etc.
        /// It will also remove items from the scene if they incur too
        /// much damage
        /// </summary>
        /// <param name="geom1">First geometry</param>
        /// <param name="geom2">Second geometry</param>
        /// <param name="contactList">No idea</param>
        /// <returns></returns>
        /*public static bool OnCollision(Geom geom1, Geom geom2, ContactList contactList)
        {
            
            #region GET PROPERTIES OF GEOMS
            //First retrieve the Unit Type from their
            //respected pool
            _containerTypeID1 = geom1.ContainerTID;
            _containerTypeID2 = geom2.ContainerTID;
            //Find geom1
            
            switch (_containerTypeID1)
            {
                case ContainerTypeID.BulletPool:
                    _dmg1 = BulletManager.BulletPool.Get(geom1.NodeID).Item.UType.AttackDamageCurrent;
                    _type1 = ObjectType.Bullet;
                    goto B;                    
                    break;

                case ContainerTypeID.EnemyPool:
                    _dmg1 = EnemyManager.EnemyPool.Get(geom1.NodeID).Item.UType.AttackDamageCurrent;
                    _health1 = EnemyManager.EnemyPool.Get(geom1.NodeID).Item.UType.HealthCurrent;
                    _type1 = ObjectType.Enemy;
                    goto B;
                    break;
                case ContainerTypeID.PlayerObject:
                    //todo: implement this
                    _type1 = ObjectType.Player;
                    break;
                case ContainerTypeID.TowerPool:
                    //todo: implement this
                    //will be used for firing shots
                    _type1 = ObjectType.Tower;
                    break;  
                default:
                    throw new Exception("INCORRECT CONTAINERTYPEID");
                    break;
            }

            //find geom2
            B:
            switch (_containerTypeID2)
            {
                case ContainerTypeID.BulletPool:
                    _dmg2 = BulletManager.BulletPool.Get(geom1.NodeID).Item.UType.AttackDamageCurrent;
                    _type2 = ObjectType.Bullet;                    
                    break;

                case ContainerTypeID.EnemyPool:
                    _dmg2 = EnemyManager.EnemyPool.Get(geom1.NodeID).Item.UType.AttackDamageCurrent;
                    _health2 = EnemyManager.EnemyPool.Get(geom1.NodeID).Item.UType.HealthCurrent;     
                    _type2 = ObjectType.Enemy;
                    break;
                case ContainerTypeID.PlayerObject:
                    //todo: implement this
                    _type2 = ObjectType.Player;
                    break;
                case ContainerTypeID.TowerPool:
                    //todo: implement this
                    //will be used for firing shots
                    _type2 = ObjectType.Tower;
                    break;  
                default:
                    throw new Exception("INCORRECT CONTAINERTYPEID");
                    break;
            }

            #endregion

            #region APPLY DAMAGE
            //We have four different combonations:
            //Bullet with bullet
            //bullet with enemy
            //enemy with enemy
            //enemy with bullet
            //but really only two different possibilites

            //right now bullet with bullet and enemy with enemy doesn't do damage
            if(_type1 != _type2)
            {
                //if type1 is a bullet
                if (_type1 == ObjectType.Bullet)
                {
                    //if type2 is an enemy
                    if (_type2 == ObjectType.Enemy)
                    {
                        //do damaget to enemy
                        _health2 -= _dmg1;

                    }
                }
                //if type1 is a enemy
                else if (_type1 == ObjectType.Enemy)
                {
                    if (_type2 == ObjectType.Bullet)
                    {
                        _health1 -= _dmg2;
                    }
                }                
            }
            else if (_type1 == _type2)
            {
            }
            else
            { }

            #endregion

            return true;
        }   
         */
    }
}
